home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Precision Software Appli…tions Silver Collection 1
/
Precision Software Applications Silver Collection Volume One (PSM) (1993).iso
/
tutor
/
c1tutor.exe
/
CHAP5.TXT
< prev
next >
Wrap
Text File
|
1987-07-04
|
35KB
|
784 lines
Chapter 5 - Functions, variables, and prototypes
OUR FIRST USER DEFINED FUNCTION
Load and examine the file SUMSQRES.C for an example of
a C program with functions. Actually this is not the first
function we have encountered because the "main" program we
have been using all along is technically a function, as is
the "printf" function. The "printf" function is a library
function that was supplied with your compiler.
Notice the executable part of this program. It begins
with a line that simply says "header()", which is the way to
call any function. The parentheses are required because the
C compiler uses them to determine that it is a function call
and not simply a misplaced variable. When the program comes
to this line of code, the function named "header" is called,
its statements are executed, and control returns to the
statement following this call. Continuing on we come to a
"for" loop which will be executed 7 times and which calls
another function named "square" each time through the loop,
and finally a function named "ending" will be called and
executed. For the moment ignore the "index" in the
parentheses of the call to "square". We have seen that this
program therefore calls a header, 7 square calls, and an
ending. Now we need to define the functions.
DEFINING THE FUNCTIONS
Following the main program you will see another program
that follows all of the rules set forth so far for a "main"
program except that it is named "header()". This is the
function which is called from within the main program. Each
of these statements are executed, and when they are all
complete, control returns to the main program.
The first statement sets the variable "sum" equal to
zero because we will use it to accumulate a sum of squares.
Since the variable "sum" is defined as an integer type
variable prior to the main program, it is available to be
used in any of the following functions. It is called a
"global" variable, and it's scope is the entire program and
all functions. More will be said about the scope of
variables near the end of this chapter. The next statement
outputs a header message to the monitor. Program control
then returns to the main program since there are no
additional statements to execute in this function.
It should be clear to you that the two executable lines
from this function could be moved to the main program,
replacing the header call, and the program would do exactly
the same thing that it does as it is now written. This does
not minimize the value of functions, it merely illustrates
Page 31
Chapter 5 - Functions, variables, and prototypes
the operation of this simple function in a simple way. You
will find functions to be very valuable in C programming.
PASSING A VALUE TO A FUNCTION
Going back to the main program, and the "for" loop
specifically, we find the new construct from the end of the
last lesson used in the last part of the "for" loop, namely
the "index++". You should get used to seeing this, as you
will see it a lot in C programs.
In the call to the function "square", we have an added
feature, namely the variable "index" within the parentheses.
This is an indication to the compiler that when you go to
the function, you wish to take along the value of index to
use in the execution of that function. Looking ahead at the
function "square", we find that another variable name is
enclosed in its parentheses, namely the variable "number".
This is the name we prefer to call the variable passed to
the function when we are in the function. We can call it
anything we wish as long as it follows the rules of naming
an identifier. Since the function must know what type the
variable is, it is defined following the function name but
before the opening brace of the function itself. Thus, the
line containing "int number;" tells the function that the
value passed to it will be an integer type variable. With
all of that out of the way, we now have the value of index
from the main program passed to the function "square", but
renamed "number", and available for use within the function.
This is the "classic" style of defining function variables
and has been in use since C was originally defined. A newer
method is gaining in popularity due to its many benefits and
will be discussed later in this chapter.
Following the opening brace of the function, we define
another variable "numsq" for use only within the function
itself, (more about that later) and proceed with the
required calculations. We set "numsq" equal to the square
of number, then add numsq to the current total stored in
"sum". Remember that "sum += numsq" is the same as "sum =
sum + numsq" from the last lesson. We print the number and
its square, and return to the main program.
MORE ABOUT PASSING A VALUE TO A FUNCTION
When we passed the value of "index" to the function, a
little more happened than meets the eye. We did not
actually pass the value of index to the function, we
actually passed a copy of the value. In this way the
original value is protected from accidental corruption by a
called function. We could have modified the variable
Page 32
Chapter 5 - Functions, variables, and prototypes
"number" in any way we wished in the function "square", and
when we returned to the main program, "index" would not have
been modified. We thus protect the value of a variable in
the main program from being accidentally corrupted, but we
cannot return a value to the main program from a function
using this technique. We will find a well defined method of
returning values to the main program or to any calling
function when we get to arrays and another method when we
get to pointers. Until then the only way you will be able
to communicate back to the calling function will be with
global variables. We have already hinted at global
variables above, and will discuss them in detail later in
this chapter.
Continuing in the main program, we come to the last
function call, the call to "ending". This call simply calls
the last function which has no local variables defined. It
prints out a message with the value of "sum" contained in it
to end the program. The program ends by returning to the
main program and finding nothing else to do. Compile and
run this program and observe the output.
NOW TO CONFESS A LITTLE LIE
I told you a short time ago that the only way to get a
value back to the main program was through use of a global
variable, but there is another way which we will discuss
after you load and display the file named SQUARES.C. In
this file we will see that it is simple to return a single
value from a called function to the calling function. But
once again, it is true that to return more than one value,
we will need to study either arrays or pointers.
In the main program, we define two integers and begin a
"for" loop which will be executed 8 times. The first
statement of the "for" loop is "y = squ(x);", which is a new
and rather strange looking construct. From past experience,
we should have no trouble understanding that the "squ(x)"
portion of the statement is a call to the "squ" function
taking along the value of "x" as a variable. Looking ahead
to the function itself we find that the function prefers to
call the variable "in" and it proceeds to square the value
of "in" and call the result "square". Finally, a new kind
of a statement appears, the "return" statement. The value
within the parentheses is assigned to the function itself
and is returned as a usable value in the main program.
Thus, the function call "squ(x)" is assigned the value of
the square and returned to the main program such that "y" is
then set equal to that value. If "x" were therefore
assigned the value 4 prior to this call, "y" would then be
set to 16 as a result of this line of code.
Page 33
Chapter 5 - Functions, variables, and prototypes
Another way to think of this is to consider the
grouping of characters "squ(x)" as another variable with a
value that is the square of "x", and this new variable can
be used any place it is legal to use a variable of its type.
The values of "x" and "y" are then printed out.
To illustrate that the grouping of "squ(x)" can be
thought of as just another variable, another "for" loop is
introduced in which the function call is placed in the print
statement rather than assigning it to a new variable.
One last point must be made, the type of variable
returned must be defined in order to make sense of the data,
but the compiler will default the type to integer if none is
specified. If any other type is desired, it must be
explicitly defined. How to do this will be demonstrated in
the next example program.
Compile and run this program which also uses the
"classic" method of defining function variables.
FLOATING POINT FUNCTIONS
Load the program FLOATSQ.C for an example of a function
with a floating point type of return. It begins by defining
a global floating point variable we will use later. Then in
the "main" part of the program, an integer is defined,
followed by two floating point variables, and then by two
strange looking definitions. The expressions "sqr()" and
"glsqr()" look like function calls and they are. This is
the proper way in C to define that a function will return a
value that is not of the type "int", but of some other type,
in this case "float". This tells the compiler that when a
value is returned from either of these two functions, it
will be of type "float". This is, once again, the "classic"
method of defining functions.
Now refer to the function "sqr" near the center of the
listing and you will see that the function name is preceded
by the name "float". This is an indication to the compiler
that this function will return a value of type "float" to
any program that calls it. The function is now compatible
with the call to it. The line following the function name
contains "float inval;", which indicates to the compiler
that the variable passed to this function from the calling
program will be of type "float".
The next function, namely "glsqr", will also return a
"float" type variable, but it uses a global variable for
input. It also does the squaring right within the return
Page 34
Chapter 5 - Functions, variables, and prototypes
statement and therefore has no need to define a separate
variable to store the product.
The overall structure of this program should pose no
problem and will not be discussed in any further detail. As
is customary with all example programs, compile and run this
program.
THERE IS A BUG IN THE FIRST VERSION OF TURBO C
When you run this program, using version 1.0 of Turbo
C, you will find that the function named "sqr()" will return
a value of zero. This is because it receives a zero from
the calling program. The program is written correctly but
the compiler has a bug in it. A call to Borland resulted in
a fix by simply using the "modern" method of function
definition rather than the "classic" which has been used to
this point. As you read articles on C, you will see
programs written in the "classic" style, so you need to be
capable of reading them. It would be highly recommended,
however, that you learn and use the "modern" method which
will be covered shortly in this tutorial. We will return to
this program and show how to fix it so that it will work in
spite of the bug.
There will be times that you will have a need for a
function to return a pointer as a result of some
calculation. There is a way to define a function so that it
does just that. We haven't studied pointers yet, but we
will soon. This is just a short preview of things to come.
SCOPE OF VARIABLES
Load the next program, SCOPE.C, and display it for a
discussion of the scope of variables in a program. Ignore
the 4 statements in lines 2 through 5 of this program for a
few moments, and we will discuss them later.
The first variable defined is a global variable "count"
which is available to any function in the program since it
is defined before any of the functions. In addition, it is
always available because it does not come and go as the
program is executed. (That will make sense shortly.)
Farther down in the program, another global variable named
"counter" is defined which is also global but is not
available to the main program since it is defined following
the main program. A global variable is any variable that is
defined outside of any function. Note that both of these
variables are sometimes referred to as external variables
because they are external to any functions.
Page 35
Chapter 5 - Functions, variables, and prototypes
Return to the main program and you will see the
variable "index" defined as an integer. Ignore the word
"register" for the moment. This variable is only available
within the main program because that is where it is defined.
In addition, it is an "automatic" variable, which means that
it only comes into existence when the function in which it
is contained is invoked, and ceases to exist when the
function is finished. This really means nothing here
because the main program is always in operation, even when
it gives control to another function. Another integer is
defined within the "for" braces, namely "stuff". Any
pairing of braces can contain a variable definition which
will be valid and available only while the program is
executing statements within those braces. The variable will
be an "automatic" variable and will cease to exist when
execution leaves the braces. This is convenient to use for
a loop counter or some other very localized variable.
MORE ON "AUTOMATIC" VARIABLES
Observe the function named "head1" in line 26 which
looks a little funny because of "void" being used twice.
The purpose of the uses of the word "void" will be explained
shortly. The function contains a variable named "index",
which has nothing to do with the "index" of the main
program, except that both are automatic variables. When the
program is not actually executing statements in this
function, this variable named "index" does not even exist.
When "head1" is called, the variable is generated, and when
"head1" completes its task, the variable "index" is
eliminated completely from existence. Keep in mind however
that this does not affect the variable of the same name in
the main program, since it is a completely separate entity.
Automatic variables therefore, are automatically
generated and disposed of when needed. The important thing
to remember is that from one call to a function to the next
call, the value of an automatic variable is not preserved
and must therefore be reinitialized.
WHAT ARE STATIC VARIABLES?
An additional variable type must be mentioned at this
point, the "static" variable. By putting the reserved word
"static" in front of a variable declaration within a
function, the variable or variables in that declaration are
static variables and will stay in existence from call to
call of the particular function.
By putting the same reserved word in front of an
external variable, one outside of any function, it makes the
Page 36
Chapter 5 - Functions, variables, and prototypes
variable private and not accessible to use in any other
file. This implies that it is possible to refer to external
variables in other separately compiled files, and that is
true. Examples of this usage will be given in chapter 14 of
this tutorial.
USING THE SAME NAME AGAIN
Refer to the function named "head2". It contains
another definition of the variable named "count". Even
though "count" has already been defined as a global
variable, it is perfectly all right to reuse the name in
this function. It is a completely new variable that has
nothing to do with the global variable of the same name, and
causes the global variable to be unavailable in this
function. This allows you to write programs using existing
functions without worrying about what names were used for
variables in the functions because there can be no conflict.
You only need to worry about the variables that interface
with the functions.
WHAT IS A REGISTER VARIABLE?
Now to fulfill a promise made earlier about what a
register variable is. A computer can keep data in a
register or in memory. A register is much faster in
operation than memory but there are very few registers
available for the programmer to use. If there are certain
variables that are used extensively in a program, you can
designate that those variables are to be stored in a
register if possible in order to speed up the execution of
the program. Turbo C allows you to use 2 register variables
and will ignore additional requests if you request more than
2.
Turbo C allows you to use register variables for short
int and int types along with their unsigned counterparts. It
also allows you to use register storage for two byte
pointers which we will study in about 3 chapters.
WHERE DO I DEFINE VARIABLES?
Now for a refinement on a general rule stated earlier.
When you have variables brought to a function as arguments
to the function, and you are using the "classic" style of
programming, they are defined immediately after the function
name and prior to the opening brace for the program. Other
variables used in the function are defined at the beginning
of the function, immediately following the opening brace of
the function, and before any executable statements.
Page 37
Chapter 5 - Functions, variables, and prototypes
WHAT IS PROTOTYPING?
A prototype is a "model" of the real thing and when
programming in Turbo C, you have the ability to define a
"model" of each function for the compiler. The compiler can
then use the "model" to check each of your calls to the
function and determine if you have used the correct number
of arguments in the function call and if they are of the
correct type. By using prototypes, you let the compiler do
some additional error checking for you. The ANSI standard
for C should be released late in 1987, and will contain
prototyping as part of its recommended standard. Every good
C compiler will have prototyping available, so you should
learn to use it.
Returning to lines 3, 4, and 5 in SCOPE.C, we have the
prototypes for the three functions contained within the
program. The first "void" in each line tells the compiler
that these particular functions do not return a value, so
that the compiler would flag the statement index = head1();
as an error because nothing is returned to assign to the
variable "index". The word "void" within the parentheses
tells the compiler that this function requires no parameters
and if a variable were included, it would be an error and
the compiler would issue a warning message. If you wrote
the statement head1(index);, it would be a error. This
allows you to use type checking when programming in C in
much the same manner that it is used in Pascal, Modula 2, or
Ada.
You should enable prototype checking with Turbo C at
this point to see how well it works. In the "Option" menu
select "Compiler", then "Errors", followed by "Less common
errors", then turn on "F: Call to function with no
prototype" by hitting the "F" key. The program SCOPE.C will
not produce any warnings, but RECURSON.C which is next will
result in some warning messages.
Line 2 of SCOPE.C tells the system to go to the include
files and get the file named STDIO.H which contains the
prototypes for the standard input and output functions so
they can be checked for proper variable types. Don't worry
about the "include" yet, it will be covered in detail later
in this tutorial.
Compile and run this program and, when you return to
the Integrated Environment, observe that there are no
warning messages.
Page 38
Chapter 5 - Functions, variables, and prototypes
STANDARD FUNCTION LIBRARIES
Every compiler comes with some standard predefined
functions which are available for your use. These are
mostly input/output functions, character and string
manipulation functions, and math functions. We will cover
most of these in subsequent chapters. Prototypes are
defined for you by the Turbo C compiler writer for all of
the functions that are included with your compiler. A few
minutes spent studying your Turbo C Reference Guide will
give you an insight in where the prototypes are defined for
each of the functions.
In addition, most compilers have additional functions
predefined that are not standard but allow the programmer to
get the most out of his particular computer. In the case of
the IBM-PC and compatibles, most of these functions allow
the programmer to use the BIOS services available in the
operating system, or to write directly to the video monitor
or to any place in memory. These will not be covered in any
detail as you will be able to study these unique aspects of
the Turbo C compiler on your own. The entire Turbo C
Reference Guide is devoted to defining functions. Many of
these kinds of functions are used in the example programs in
chapter 14.
WHAT IS RECURSION?
Recursion is another of those programming techniques
that seem very intimidating the first time you come across
it, but if you will load and display the example program
named RECURSON.C, we will take all of the mystery out of it.
This is probably the simplest recursive program that it is
possible to write and it is therefore a stupid program in
actual practice, but for purposes of illustration, it is
excellent.
Recursion is nothing more than a function that calls
itself. It is therefore in a loop which must have a way of
terminating. In the program on your monitor, the variable
"index" is set to 8, and is used as the argument to the
function "count_dn". The function simply decrements the
variable, prints it out in a message, and if the variable is
not zero, it calls itself, where it decrements it again,
prints it, etc. etc. etc. Finally, the variable will reach
zero, and the function will not call itself again. Instead,
it will return to the prior time it called itself, and
return again, until finally it will return to the main
program and will return to DOS.
Page 39
Chapter 5 - Functions, variables, and prototypes
For purposes of understanding you can think of it as
having 8 copies of the function "count_dn" available and it
simply called all of them one at a time, keeping track of
which copy it was in at any given time. That is not what
actually happened, but it is a reasonable illustration for
you to begin understanding what it was really doing.
WHAT DID IT DO?
A better explanation of what actually happened is in
order. When you called the function from itself, it stored
all of the variables and all of the internal flags it needs
to complete the function in a block somewhere. The next
time it called itself, it did the same thing, creating and
storing another block of everything it needed to complete
that function call. It continued making these blocks and
storing them away until it reached the last function when it
started retrieving the blocks of data, and using them to
complete each function call. The blocks were stored on an
internal part of the computer called the "stack". This is a
part of memory carefully organized to store data just as
described above. It is beyond the scope of this tutorial to
describe the stack in detail, but it would be good for your
programming experience to read some material describing the
stack. A stack is used in nearly all modern computers for
internal housekeeping chores.
In using recursion, you may desire to write a program
with indirect recursion as opposed to the direct recursion
described above. Indirect recursion would be when a
function "A" calls the function "B", which in turn calls
"A", etc. This is entirely permissible, the system will
take care of putting the necessary things on the stack and
retrieving them when needed again. There is no reason why
you could not have three functions calling each other in a
circle, or four, or five, etc. The C compiler will take
care of all of the details for you.
The thing you must remember about recursion is that at
some point, something must go to zero, or reach some
predefined point to terminate the loop. If not, you will
have an infinite loop, and the stack will fill up and
overflow, giving you an error and stopping the program
rather abruptly.
Compile and run this program. If you left the
prototype checking on from the last program you will find
several "warnings" in the message window when you return
to the Integrated Environment. Study these for a few
minutes. One of the suggested exercises at the end of this
Page 40
Chapter 5 - Functions, variables, and prototypes
chapter is to modify this program to eliminate the prototype
warnings.
ANOTHER EXAMPLE OF RECURSION
The program named BACKWARD.C is another example of
recursion, so load it and display it on your screen. This
program is similar to the last one except that it uses a
character array. Each successive call to the function named
"forward_and_backwards" causes one character of the message
to be printed. Additionally, each time the function ends,
one of the characters is printed again, this time backwards
as the string of recursive function calls is retraced.
This program uses the "modern" method of function
definition and includes full prototype definitions. The
"modern" method of function definition moves the types of
the variables into the parentheses along with the variable
names themselves. The final result is that the line
containing the function name looks more like the
corresponding line in Pascal, Modula 2, or Ada.
Don't worry about the character array defined in line 9
or the other new material presented here. After you
complete chapter 7 of this tutorial, this program will make
sense. It was felt that introducing a second example of
recursion was important so this file is included here.
Compile and run this program with prototype checking
on, and observe the results.
HOW TO WORK AROUND THE TURBO C (v1.0) BUG
Load and display the program named FLOATSQ2.C which is
an exact copy of the program FLOATSQ.C which we considered
earlier with prototyping added. The return of the erroneous
zeros is now repaired and the correct values are returned.
Apparently, when the coders of Turbo C at Borland were
testing this compiler, they used prototyping and never found
this bug. The use of prototyping is a good practice for all
C programmers to get into.
Several things should be mentioned about this program.
First, the word "float" at the beginning of lines 27 and 35
indicate to the compiler that these functions are functions
that return "float" type values. Also, since the prototypes
are given before "main", the functions are not required to
be identified in line 12 as they were in line 7 of FLOATSQ.C
earlier in this chapter. They can be included in line 12,
but they are not required to be.
Page 41
Chapter 5 - Functions, variables, and prototypes
Notice also that the type of the variable "inval" is
included within the parentheses in line 27. It would be
very educational for you to modify this program so that you
included a call to "sqr" with a variable of type "int"
within the parentheses to see what kind of a warning you
would get. Do the same thing in the program without
prototype checking, FLOATSQ.C.
PROGRAMMING EXERCISES
1. Rewrite TEMPCONV.C, from an earlier chapter, and move
the temperature calculation to a function.
2. Write a program that writes your name on the monitor 10
times by calling a function to do the writing. Move the
called function ahead of the "main" function to see if
the Turbo C compiler will allow it.
3. Add prototyping to the program named RECURSON.C to
eliminate the warnings.
Page 42